home *** CD-ROM | disk | FTP | other *** search
/ Experimental BBS Explossion 3 / Experimental BBS Explossion III.iso / c / bc_pas_1.zip / PCMIOC.C < prev    next >
Text File  |  1993-01-06  |  50KB  |  1,812 lines

  1. /*$Author:   DCODY  $*/
  2. /*$Date:   06 Jan 1993 15:26:34  $*/
  3. /*$Header:   X:/sccs/pcm/pcmioc.c_v   1.11   06 Jan 1993 15:26:34   DCODY  $*/
  4. /*$Log:   X:/sccs/pcm/pcmioc.c_v  $
  5.  * 
  6.  *    Rev 1.11   06 Jan 1993 15:26:34   DCODY
  7.  * corrected two bugs: ContinueThisBlockOutput had a bug in an IF statement
  8.  * where it evaluated the two expressions incorrectly. The second bug was
  9.  * in QueryPCMStream, which returned an incorrect value. 
  10.  * Also, some debugging info was added, but commented out.
  11.  * 
  12.  *    Rev 1.10   08 Dec 1992 17:13:54   DCODY
  13.  * added a new routine: QueryPCMStream to return the number of blocks
  14.  * buffered.
  15.  * Also added, but commented out some debugging code.
  16.  * 
  17.  *    Rev 1.9   20 Oct 1992 10:07:38   DCODY
  18.  * lots of cosmetic changes. all variables are now initialized so they
  19.  * have memory allocated at compile time.
  20.  * 
  21.  *    Rev 1.8   06 Oct 1992 15:59:50   DCODY
  22.  * major changes so code is free (freer) from the C libraries. Replaced
  23.  * malloc and fread/fwrite.
  24.  * 
  25.  *    Rev 1.7   01 Oct 1992 12:05:02   DCODY
  26.  * next stage of completion for PlayThisBlock, RecordThisBlock, etc.
  27.  * 
  28.  *    Rev 1.6   23 Sep 1992 10:56:34   DCODY
  29.  * more work on playthisblock, continuethisblock...
  30.  * 
  31.  *    Rev 1.5   26 Aug 1992 10:57:30   DCODY
  32.  * Added Playthisblock and RecordThisBlock
  33.  * 
  34.  *    Rev 1.4   12 Aug 1992 17:10:30   DCODY
  35.  * major change to eliminate the foreground buffers.
  36.  * 
  37.  *    Rev 1.3   24 Jul 1992 15:36:14   DCODY
  38.  * changed _fmemcpy to _rfmemcpy
  39.  * 
  40.  *    Rev 1.2   17 Jul 1992 14:22:50   DCODY
  41.  * InitMVSound() now performed within OpenPCMBuffering().
  42.  * 
  43.  *    Rev 1.1   23 Jun 1992 17:11:42   DCODY
  44.  * PAS2 update
  45.  * 
  46.  *    Rev 1.0   15 Jun 1992 09:44:38   BCRANE
  47.  * Initial revision.
  48. */
  49. /*$Logfile:   X:/sccs/pcm/pcmioc.c_v  $*/
  50. /*$Modtimes$*/
  51. /*$Revision:   1.11  $*/
  52. /*$Workfile:   pcmioc.c  $*/
  53.  
  54.  
  55. ;    /*\
  56. ;---|*|----====< PCMIOC.C >====----
  57. ;---|*|
  58. ;---|*| These routines maintain DMA controlled I/O of the Audio Spectrum
  59. ;---|*|
  60. ;---|*| Copyright (c) 1991, Media Vision, Inc. All rights reserved.
  61. ;---|*|
  62. ;    \*/
  63.  
  64. #include <stdio.h>
  65. #include <stdlib.h>
  66.  
  67. #include "pcmio.h"
  68. #include "common.h"
  69. #include "mvsound.h"
  70.  
  71. ;    /*\
  72. ;---|*|-----------====< T H E O R Y   O F    O P E R A T I O N >====------------
  73. ;---|*|
  74. ;---|*| The best DMA controlled PCM output requires a continuous stream of data
  75. ;---|*| to be available in a real-time environment.
  76. ;---|*|
  77. ;---|*| DMA controlled PCM input, with the same real-time requirements, needs
  78. ;---|*| to be able to keep storing data into memory without pausing.
  79. ;---|*|
  80. ;---|*| The following approach is designed to allow the DMA to be setup in
  81. ;---|*| "auto-initialize" mode, thereby guarenteeing continuous play/record.
  82. ;---|*|
  83. ;---|*| To keep the DMA running, multiple divisions of the DMA buffer are
  84. ;---|*| used to keep the data moving. Due to the fact that DOS is neither
  85. ;---|*| a real-time, or re-entrant operating system, this code divides up
  86. ;---|*| the buffer management tasks into a "foreground" and "background" task.
  87. ;---|*|
  88. ;---|*| A sample buffer count timer on the Audio Spectrum is used to interrupt
  89. ;---|*| the CPU when a DMA buffer division has filled or emptied. For our
  90. ;---|*| purposes here, this amount may be 1/2, 1/4, 1/8th or some smaller
  91. ;---|*| division of the whole DMA buffer. Note: judgement must be used here
  92. ;---|*| in selecting the DMA buffer size, and the integral division. Too small
  93. ;---|*| of an integral may result in broken DMA I/O. A buffer too large never
  94. ;---|*| hurts anything. (it just reduces the amount of available memory).
  95. ;---|*|
  96. ;---|*|                 ----====< PCM OUTPUT >====----
  97. ;---|*|
  98. ;---|*| To perform PCM output ("play"), A linked list of buffer pointers is
  99. ;---|*| used to fill the DMA buffer by the foregound task. As the DMA runs,
  100. ;---|*| it will send the buffer contents to the audio card. Here is a visual:
  101. ;---|*|
  102. ;---|*|                            
  103. ;---|*|                      Foreground Loads
  104. ;---|*|                        the Top Level
  105. ;---|*|                          Buffers
  106. ;---|*|                            
  107. ;---|*|                        ┌─┬─┬─┬─┬─┐
  108. ;---|*|      DMA Level Buffers │ │ │ │ │ │
  109. ;---|*|                        └─┴─┴─┴─┴─┘
  110. ;---|*|                            
  111. ;---|*|                        ┌─────────┐
  112. ;---|*|                        │hardware │
  113. ;---|*|                        └╥╥╥──────┘
  114. ;---|*|
  115. ;---|*| To actually start the output, the foreground task loads it's
  116. ;---|*| buffers, then starts the DMA to play the buffer. The background
  117. ;---|*| task only indicates when each block is played. It will shut down
  118. ;---|*| the DMA if no more data is present in the buffers.
  119. ;---|*|
  120. ;---|*| If the foreground task can keep the linked list of buffers full,
  121. ;---|*| there should be non-stop PCM output (Good!). If the foreground task
  122. ;---|*| does not keep up, the background task will be forced to stop the
  123. ;---|*| the DMA, thereby causing a break in the output (Bad!). Once the DMA
  124. ;---|*| has stopped, the foreground task will have to restart the DMA a
  125. ;---|*| second time to continue the flow of data.
  126. ;---|*|
  127. ;---|*|                 ----====< PCM INPUT >====----
  128. ;---|*|
  129. ;---|*| To perform PCM input ("record"), the same linked list of buffers
  130. ;---|*| are also used. This buffer is filled with sampled data from the
  131. ;---|*| hardware. The background process will increment a global variable for
  132. ;---|*| each buffer filled. The foreground routine must extract each buffer
  133. ;---|*| and process it (copy to memory, or write it to disk). Here is a visual:
  134. ;---|*|
  135. ;---|*|                            
  136. ;---|*|                      Foreground unloads
  137. ;---|*|                        the Top Level
  138. ;---|*|                          Buffers
  139. ;---|*|                            
  140. ;---|*|                        ┌─┬─┬─┬─┬─┐
  141. ;---|*|      DMA Level Buffers │ │ │ │ │ │
  142. ;---|*|                        └─┴─┴─┴─┴─┘
  143. ;---|*|                            
  144. ;---|*|                        ┌─────────┐
  145. ;---|*|                        │hardware │
  146. ;---|*|                        └╥╥╥──────┘
  147. ;---|*|
  148. ;---|*| To actually start the input, the foreground starts the DMA running to
  149. ;---|*| begin the transfer. The background task increments the global variable
  150. ;---|*| as each interrupt occurs. If all the buffers are full, the DMA transfer
  151. ;---|*| is terminated. The foreground routine must poll this variable to keep
  152. ;---|*| the data moving out of the DMA buffer.
  153. ;---|*|
  154. ;---|*| If the foreground task can keep the linked list of buffers empty,
  155. ;---|*| there should be non-stop PCM input (Good!). If the foreground task
  156. ;---|*| does not keep up, the background task will be forced to stop the
  157. ;---|*| the DMA, thereby causing a break in the input (Bad!). Once the DMA
  158. ;---|*| has stopped, the foreground task will have to restart the DMA tranfer
  159. ;---|*| a second time to restart the DMA.
  160. ;---|*|
  161. ;---|*|                 ----====< DATA VARIABLES >====----
  162. ;---|*|
  163. ;---|*| The following is a description of the variables shared between the
  164. ;---|*| foreground and background tasks. There are three global variables,
  165. ;---|*| and a linked list of buffers shared between the two tasks.
  166. ;---|*|
  167. ;---|*| The linked list of buffers uses a "header" to each buffer. This
  168. ;---|*| header holds the information for linking to the next buffer, whether
  169. ;---|*| the buffer is full or empty, and the count of bytes in the buffer.
  170. ;---|*|
  171. ;---|*| typedef struct _buffptr {
  172. ;---|*|     int status;                 /* 0=empty, 1=full                 * /
  173. ;---|*|     int count;                    /* # of bytes in the buffer      * /
  174. ;---|*|     int size;                    /* total size of read data         * /
  175. ;---|*|     char huge *buffer;            /* pointer to buffer data         * /
  176. ;---|*|     struct _buffptr, *nextptr;    /* pointer to next buffer         * /
  177. ;---|*|
  178. ;---|*| } BuffData,*BuffPtr;
  179. ;---|*|
  180. ;---|*| BuffPtr HeadOfBuffers;            /* global variable head pointer  * /
  181. ;---|*| int BufferDataCount;            /* # of full DMA buffers parts     * /
  182. ;---|*| int DMARunning;                 /* DMA status (0=off,1=running)  * /
  183. ;---|*| char far *StartOfDMABuffer;     /* start of actual DMA buffer     * /
  184. ;---|*| int ProcessedBlockCount;        /* # of blocks DMA handled         * /
  185. ;---|*|
  186. ;---|*| "HeadOfBuffers" points to the first buffer in the linked list.
  187. ;---|*|
  188. ;---|*|       This linked list is made up of structures containing the buffer
  189. ;---|*|     data and other information. The last entry in the list points
  190. ;---|*|     back to the first entry, thereby creating a circular linked
  191. ;---|*|     list.
  192. ;---|*|       Each buffer has a status word: 0=empty,1=full.
  193. ;---|*|       The count indicates the # of bytes in the buffer. This count
  194. ;---|*|     is used to communication between the foreground and background
  195. ;---|*|     process that data is available. For output, the count tells the
  196. ;---|*|     background that data is available to be loaded in the DMA buffer.
  197. ;---|*|     For input, the count tells the foreground process that there is
  198. ;---|*|     data to be written to disk.
  199. ;---|*|
  200. ;---|*| "BufferDataCount" is the key handshaking variable between the
  201. ;---|*|     foreground and background processes. It indicates how many DMA
  202. ;---|*|     buffers divisions contain data.
  203. ;---|*|
  204. ;---|*|     For output, it holds a count of DMA divisions hold data. This
  205. ;---|*|     global variable is incremented each time a buffer is loaded by
  206. ;---|*|     the foreground task, and decremented when a buffer is emptied
  207. ;---|*|     by the background task.
  208. ;---|*|
  209. ;---|*|     For input, it holds the number of buffers with data in the DMA
  210. ;---|*|     buffer. It is incremented by the background process and
  211. ;---|*|     decremented by the foreground process.
  212. ;---|*|
  213. ;---|*| "DMARunning" is set to true or false depending upon the state
  214. ;---|*|     of the DMA channel. It is set TRUE when the DMA is running (either
  215. ;---|*|     playing or recording), and FALSE when the DMA is turned off.
  216. ;---|*|
  217. ;---|*| "ProcessedBlockCount" is the running total of blocks the DMA has
  218. ;---|*|     processed from the last Start I/O call.
  219. ;---|*|
  220. ;---|*|     For input, this is the total number of dma divisions filled
  221. ;---|*|     by the DMA.
  222. ;---|*|
  223. ;---|*|     For output, this is the total number of blocks loaded into
  224. ;---|*|     the DMA buffer.
  225. ;---|*|
  226. ;---|*| "StartOfDMABuffer" points to the first byte of the DMA circular buffer.
  227. ;---|*|
  228. ;---|*| The following routines provide a high level interface to DMA driven
  229. ;---|*| PCM output:
  230. ;---|*|
  231. ;---|*| int  OpenPCMBuffering  ( int, int, int, int )
  232. ;---|*|
  233. ;---|*|         This routine is the first routine to be called. It sets
  234. ;---|*|         up the DMA channel, IRQ, and allocates memory for the buffers.
  235. ;---|*|
  236. ;---|*| int PCMState ( int, int, int, int )
  237. ;---|*|
  238. ;---|*|         This routine passes in the sample rate, stereo/mono flag,
  239. ;---|*|         the compression type (0 for 8 bit, 1 for for 4 bit),
  240. ;---|*|         and the PCM data sample size (8 or 16).
  241. ;---|*|
  242. ;---|*| int  StartFileInput    ( FILE *f )
  243. ;---|*|
  244. ;---|*|         This routine begins recording the PCM data to the disk file.
  245. ;---|*|         The routine returns immediately. The routine,
  246. ;---|*|         "ContinueFileInput" must be called to continue moving data
  247. ;---|*|         from the DMA buffer to to the disk.
  248. ;---|*|
  249. ;---|*| int  StartBlockInput   (  )
  250. ;---|*|
  251. ;---|*|         This routine begins recording the PCM data. The routine
  252. ;---|*|         returns immediately. Subsequent call must be made to
  253. ;---|*|         "ContinueBlockInput" to receive data from the DMA buffer.
  254. ;---|*|
  255. ;---|*| int  StartFileOutput   ( FILE *f, long )
  256. ;---|*|
  257. ;---|*|         This routine begins playing the PCM data from the disk file.
  258. ;---|*|         The routine returns immediately. The routine,
  259. ;---|*|         "ContinueFileOutput" must be called to continue moving data
  260. ;---|*|         from the disk to the DMA buffer. The long variable tells how
  261. ;---|*|         many bytes to play.
  262. ;---|*|
  263. ;---|*| int  StartBlockOutput  ( char far * )
  264. ;---|*|
  265. ;---|*|         This routine begins playing the caller's PCM data. The
  266. ;---|*|         routine returns immediately. The routine, "ContinueBlockOutput"
  267. ;---|*|         must subsequently be called to continue data output.
  268. ;---|*|
  269. ;---|*| int  ContinueFileInput    ( )
  270. ;---|*|
  271. ;---|*|         This routine checks to see if new data has been loaded into
  272. ;---|*|         the linked list of buffers. If so, the data is written to
  273. ;---|*|         disk, and the buffer is freed up.
  274. ;---|*|
  275. ;---|*| int  ContinueBlockInput ( char far * )
  276. ;---|*|
  277. ;---|*|         This routine checks to see if new data has been loaded into
  278. ;---|*|         the linked list of buffers. If so, the data is written to
  279. ;---|*|         the caller's buffer.
  280. ;---|*|
  281. ;---|*| int  ContinueFileOutput  ( )
  282. ;---|*|
  283. ;---|*|         This routine checks to see if the PCM hardware is
  284. ;---|*|         still playing. This routine MUST be called frequently to
  285. ;---|*|         maintain continuous PCM output.
  286. ;---|*|
  287. ;---|*| int  ContinueBlockOutput (char far *)
  288. ;---|*|
  289. ;---|*|         This routine checks to see if the PCM hardware is
  290. ;---|*|         still playing. The caller passes the next block to be
  291. ;---|*|         played. A non-zero return value indicates the block has
  292. ;---|*|         been queued up to be played. A zero value means the buffer
  293. ;---|*|         is currently full, please try again...
  294. ;---|*|
  295. ;---|*| void StopDMAIO          ( )
  296. ;---|*|
  297. ;---|*|         This routine is used to prematurely terminate PCM I/O.
  298. ;---|*|
  299. ;---|*| void ClosePCMBuffering ( )
  300. ;---|*|
  301. ;---|*|         This routine is used to close down the whole PCM I/O system.
  302. ;---|*|         This call MUST be made before the caller's program terminates.
  303. ;---|*|
  304. ;    \*/
  305.  
  306. ;    /*\
  307. ;---|*|----====< Code Generation >====----
  308. ;    \*/
  309.  
  310. #define BLOCKOUT    0        /* builds block output code only            */
  311. #define BLOCKIN     0        /* builds block input code only             */
  312. #define FILEOUT     0        /* builds file output code only             */
  313. #define FILEIN        0        /* builds file input code only                */
  314. #define COMMDATA    0        /* builds both common code and data         */
  315.  
  316. #ifdef    BUILDBO
  317. #undef  BLOCKOUT
  318. #define BLOCKOUT    1
  319. #endif
  320.  
  321. #ifdef    BUILDBI
  322. #undef  BLOCKIN
  323. #define BLOCKIN     1
  324. #endif
  325.  
  326. #ifdef    BUILDFO
  327. #undef  FILEOUT
  328. #define FILEOUT     1
  329. #endif
  330.  
  331. #ifdef    BUILDFI
  332. #undef  FILEIN
  333. #define FILEIN        1
  334. #endif
  335.  
  336. #ifdef    BUILDCO
  337. #undef    COMMDATA
  338. #define COMMDATA    1
  339. #endif
  340.  
  341. ;    /*\
  342. ;---|*|----====< common data for CODE and DATA generation >====----
  343. ;    \*/
  344.  
  345.         /* buffer linked list header structures                             */
  346.  
  347.         typedef struct _buffptr {
  348.             int status;                     /* 0=empty, 1=full                */
  349.             int count;                        /* # of bytes in the buffer     */
  350.             int size;                        /* total size of allocated buff */
  351.             char huge *buffer;                /* pointer to buffer data        */
  352.             struct _buffptr far *nextptr;    /* pointer to next buffer hdr    */
  353.  
  354.         } BuffData, far *BuffPtr;
  355.  
  356. #define NODIRECTION     0                    /* defines for DirectionFlag    */
  357. #define DMAINPUT        1
  358. #define DMAOUTPUT        2
  359.  
  360.  
  361. ;    /*\
  362. ;---|*|----====< Global Data >====----
  363. ;    \*/
  364.  
  365. #define QUEUESIZE    32                        /* 32 entries                    */
  366. #define QUEUEMASK    0x1F                    /* mask to circulate the count    */
  367.  
  368. #if COMMDATA
  369.  
  370.         unsigned int MaxBuffCount = 0;        /* # of DMA buffer divisions    */
  371.         unsigned int BufferSize = 0;        /* size of each buffer division */
  372.  
  373.     /* shared global variables between the two tasks (in pcmioa.asm)        */
  374.  
  375.         BuffPtr HeadOfBuffers = 0;            /* global variable head pointer */
  376.         int BufferDataCount   = 0;            /* # of full buffers (0=done)    */
  377.         int DMARunning          = 0;            /* DMA status (0=off,1=running) */
  378.         char huge *DMABuffPtr = 0;            /* 128k+1 DMA buffer pointer    */
  379.         char far  *StartOfDMABuffer = 0;    /* start of DMA buffer pointer    */
  380.         int ProcessedBlockCount = 0;        /* # of I/O blocks processed    */
  381.         unsigned long _file_data_length = 0;/* size of data output            */
  382.         char __pcmdatasize      = 8;            /* default to 8 bit pcm         */
  383.  
  384.         FILE *__fptr = 0;                   /* file pointer for disk I/O    */
  385.         long __fptrpos = 0;                 /* file pointer position        */
  386.         BuffPtr __NextPtr = 0;                /* next buffer pointer            */
  387.         int __DirectionFlag = 0;            /* current I/O direction        */
  388.         char far * __singleblockpointer = 0;/* single shot users buffer     */
  389.  
  390.     /* local data for this body of code, but needs to be public             */
  391.  
  392.         int VoiceActivatedSavedCount = 0;    /* # of I/O blocks saved        */
  393.  
  394.         int  __queuein      = 0;
  395.         int  __queueincnt = 0;
  396.         int  __queueout   = 0;
  397.         long __queuedata  = 0;
  398.  
  399.         char far *__queuebuff[QUEUESIZE] =    // number of queued blocks
  400.             { 0 };
  401.         long __queuelen[QUEUESIZE] =        // queued block lengths
  402.             { 0 };
  403.         void (far * __queuecb[QUEUESIZE])()=// queue of callback routines
  404.             { 0 };
  405.         void (far *__synccallback)() = 0;    // callback to user code
  406.  
  407. #else
  408.  
  409.         extern unsigned int MaxBuffCount;    /* # of DMA buffer divisions    */
  410.         extern unsigned int BufferSize;     /* size of each buffer division */
  411.  
  412.     /* shared global variables between the two tasks (in pcmioa.asm)        */
  413.  
  414.         extern BuffPtr HeadOfBuffers;        /* global variable head pointer */
  415.         extern int BufferDataCount;         /* # of full buffers (0=done)    */
  416.         extern int DMARunning;                /* DMA status (0=off,1=running) */
  417.         extern char huge *DMABuffPtr;        /* 128k+1 DMA buffer pointer    */
  418.         extern char far *StartOfDMABuffer;    /* start of DMA buffer pointer    */
  419.         extern int ProcessedBlockCount;     /* # of I/O blocks processed    */
  420.         extern unsigned long _file_data_length; /* size of data output        */
  421.         extern char __pcmdatasize;            /* default to 8 bit pcm         */
  422.  
  423.         extern FILE *__fptr;                /* file pointer for disk I/O    */
  424.         extern long __fptrpos;                /* file pointer position        */
  425.         extern BuffPtr __NextPtr;            /* next buffer pointer            */
  426.         extern int __DirectionFlag;         /* current I/O direction        */
  427.         extern char far* __singleblockpointer;/* single shot users buffer  */
  428.  
  429.         extern int VoiceActivatedSavedCount;/* # of I/O blocks saved        */
  430.  
  431.         extern int    __queuein;
  432.         extern int    __queueincnt;
  433.         extern int    __queueout;
  434.         extern long __queuedata;
  435.  
  436.         extern char far *__queuebuff[];     // number of queued blocks
  437.         extern long __queuelen[];            // queued block lengths
  438.         extern void (far * __queuecb[])();    // queue of callback routines
  439.  
  440.         extern void (far *__synccallback)();// callback to user code
  441.  
  442. extern int __debugdw ();
  443.  
  444. #endif
  445.  
  446.     /* additional prototypes                                                */
  447.  
  448.         void far  * _rfmemcpy          ( void far *, void far *, unsigned int );
  449.         void huge * _rfhmemcpy          ( void huge *,void huge *,unsigned int );
  450.         void far  * _memmalloc          ( long );
  451.         void        _memmfree          ( void far * );
  452.         int         _dofread          ( char far *, int, int );
  453.         int         _dofwrite          ( char far *, int, int );
  454.  
  455. #if BLOCKOUT
  456.         static int _loadtheblock  ( char far * );
  457. #endif
  458.  
  459. #if FILEOUT
  460.         static int _loadthebuffer ();
  461. #endif
  462.  
  463.  
  464.  
  465. ;    /*\
  466. ;---|*|-----------------====================================-----------------
  467. ;---|*|-----------------====< Start of Executable Code >====-----------------
  468. ;---|*|-----------------====================================-----------------
  469. ;    \*/
  470.  
  471. #if FILEIN
  472. ;    /*\
  473. ;---|*|----====< ASpecialContinueFileInput >====----
  474. ;---|*|
  475. ;---|*| This is a special adaptation of the standard, "ContinueDMAInput"
  476. ;---|*| routine. It will check the noise level in each block before writting
  477. ;---|*| it out to disk. This way, no data is written until a noise level
  478. ;---|*| is reached.
  479. ;---|*|
  480. ;    \*/
  481. int ASpecialContinueFileInput(noise,goflag)
  482.     int noise;    /* offset from silence                                    */
  483.     int goflag; /* record all after first block                         */
  484. {
  485. int temp;
  486.  
  487.     /* if BufferDataCount is non-zero, we must process the DMA data     */
  488.  
  489.         while (BufferDataCount) {
  490.  
  491.             /* validate the level of noise before writing  it to disk    */
  492.  
  493.                 if (MakeHalfHistoGram(__NextPtr->buffer,BufferSize,noise) ||
  494.                     (VoiceActivatedSavedCount && goflag) ) {
  495.  
  496.                     /* if not all data is written, return in error        */
  497.  
  498.                     if (_dofwrite (__NextPtr->buffer,BufferSize,fileno(__fptr)) != BufferSize) {
  499.                         StopDMAIO();
  500.                         return (0);
  501.                     }
  502.  
  503.                     VoiceActivatedSavedCount++;
  504.                 }
  505.                 else
  506.                     ProcessedBlockCount--;
  507.  
  508.             /* move to the next buffer                                    */
  509.  
  510.                 __NextPtr->count = __NextPtr->status = 0;
  511.                 __NextPtr = __NextPtr->nextptr;
  512.                 BufferDataCount--;
  513.         }
  514.  
  515.     /* if at the end of this block, reposition the stream pointer        */
  516.  
  517.         if (!DMARunning)
  518.             fseek ( __fptr, __fptrpos, SEEK_SET );
  519.  
  520.         return (DMARunning);
  521. }
  522. #endif
  523.  
  524.  
  525. #if COMMDATA
  526. ;    /*\
  527. ;---|*|----====< ClosePCMBuffering >====----
  528. ;---|*|
  529. ;---|*| Removes the PCM system & deallocates the buffer memory. There is
  530. ;---|*| no return value.
  531. ;---|*|
  532. ;    \*/
  533. void ClosePCMBuffering()
  534. {
  535. BuffPtr p,op;
  536.  
  537. //__debugdw (0x01);
  538.  
  539.     /* we will kill the DMA low level processing                        */
  540.  
  541.         StopDMAIO();
  542.         _unloadirqvector();
  543.  
  544.     /* Free up the linked list of buffers                                */
  545.  
  546.         if ((p = HeadOfBuffers) != 0) {
  547.  
  548.             do {
  549.                 op    = p;                /* save the old ptr             */
  550.                 p    = p->nextptr;        /* point to the next buffer     */
  551.                 _memfree (op);            /* free up the old header        */
  552.  
  553.             } while ( (p != HeadOfBuffers) && p );
  554.        }
  555.  
  556.     /* free up the DMA buffer                                            */
  557.  
  558.         if (DMABuffPtr)
  559.             _memfree (DMABuffPtr);
  560.  
  561.     /* null it all out...                                                */
  562.  
  563.         DMABuffPtr         = 0;
  564.         HeadOfBuffers     = 0;
  565.         StartOfDMABuffer = 0;
  566.         BufferDataCount  = BufferSize = DMARunning = 0;
  567.  
  568. }
  569. #endif
  570.  
  571.  
  572. #if BLOCKIN
  573. ;    /*\
  574. ;---|*|----====< ContinueBlockInput >====----
  575. ;---|*|
  576. ;---|*| This routine checks to see if another buffer can be stored in memory.
  577. ;---|*| if so, it will load copy the DMA buffer to the caller's local buffer,
  578. ;---|*| A return value of 0 indicates the caller's buffer is empty.
  579. ;---|*|
  580. ;    \*/
  581. int ContinueBlockInput(buff)
  582.     char far *buff;
  583. {
  584.  
  585.     /* if BufferDataCount is non-zero, we must move the data to memory    */
  586.  
  587.         if (BufferDataCount) {
  588.  
  589.             /* data is available, just move it out                        */
  590.  
  591.                 _rfmemcpy (buff,__NextPtr->buffer,BufferSize);
  592.  
  593.             /* move to the next buffer                                    */
  594.  
  595.                 __NextPtr->count = __NextPtr->status = 0;
  596.                 __NextPtr = __NextPtr->nextptr;
  597.                 BufferDataCount--;
  598.  
  599.             /* returns the fact that the data has been loaded            */
  600.  
  601.                 return(1);
  602.         }
  603.         return (0);
  604. }
  605.  
  606.  
  607. ;    /*\
  608. ;---|*|----====< ContinueThisBlockInput >====----
  609. ;---|*|
  610. ;---|*| This routine extracts a DMA buffer into one or
  611. ;---|*| more target user buffers.
  612. ;---|*|
  613. ;---|*| Returns:
  614. ;---|*|    Nonzero for running & processing, else 0 for dead.
  615. ;---|*|
  616. ;    \*/
  617. int  ContinueThisBlockInput()
  618. {
  619. unsigned int n, // working integer
  620.     loop,        // loop flag to keep loading blocks
  621.     bcount;     // increments the BufferDataCount
  622. unsigned int result = 0; // holds the final count
  623.  
  624. static unsigned int TargetSize;   // remaining size of the target dma buffer
  625. static char far *dmaptr; // pointer to this DMA block
  626.  
  627.     // if the DMA is dead, give it a jump start. Bad thing, it flushes all...
  628.  
  629.         if (DMARunning == 0) {
  630.  
  631.             // blow off anything that is saved locally
  632.  
  633.                 dmaptr = 0;
  634.                 TargetSize = 0;
  635.  
  636.             // reset and restart the low level stuff...
  637.  
  638.                 _resetbuffers();
  639.                 StartTheDMAInput(ContinueThisBlockInput);
  640.  
  641.             // we have no more data, just return now
  642.  
  643.                 return(DMARunning);
  644.         }
  645.  
  646.     // if the current remaining length is null, prime for the next block
  647.  
  648.         if (_file_data_length == 0) {
  649.  
  650.             // bomb out if no data buffers queued up
  651.  
  652.             if (__queueincnt == 0)
  653.                 return(1);
  654.  
  655.             // get the next block from the queue
  656.  
  657.             _file_data_length     = __queuelen [__queueout];
  658.             __singleblockpointer = __queuebuff[__queueout];
  659.         }
  660.  
  661.     // loop here to stuff as many blocks as possible into the DMA buffer
  662.  
  663.     nextblock:
  664.  
  665.     // move up to one buffer division worth of data
  666.  
  667.         if (!TargetSize) {
  668.  
  669.             dmaptr = __NextPtr->buffer;
  670.             TargetSize = BufferSize;
  671.         }
  672.  
  673.         loop = TRUE;
  674.         bcount = 1;
  675.  
  676.     // move as many blocks as possible into the DMA buffer
  677.  
  678.         while (loop) {
  679.  
  680.             // Get the block length, up to the division size
  681.  
  682.                 if (_file_data_length <= TargetSize) {
  683.  
  684.                     n = _file_data_length;    // full target size
  685.                     _file_data_length = 0;
  686.  
  687.                 }
  688.                 else                        // partial target size
  689.  
  690.                     _file_data_length -= (n = TargetSize);
  691.  
  692.             // copy the data to the buffer, and advance the buffer that far
  693.  
  694.                 if (n) {
  695.  
  696.                     // move the recorded data into the buffer
  697.  
  698.                     __singleblockpointer
  699.                         = _rfmemcpy(__singleblockpointer,dmaptr,n);
  700.                     dmaptr += n;                // move the dma pointer
  701.                     result += n;                // more for the return value
  702.  
  703.                     __queuedata -= (n &0xffff); // less queued up
  704.                     BufferDataCount -= bcount;    // increment buffer count once
  705.                     bcount = 0;
  706.  
  707.                 }
  708.  
  709.             // if the length is zero, this buffer is done, let the caller know
  710.  
  711.                 if (!_file_data_length) {
  712.  
  713.                     // let the app. know we are done with this buffer
  714.  
  715.                         if (__queuecb[__queueout])
  716.                             (*__queuecb[__queueout])(__queuebuff[__queueout],__queuelen[__queueout]);
  717.  
  718.                         __queueincnt--;
  719.                         __queueout = ++__queueout & QUEUEMASK;
  720.  
  721.                     // Now, try to get the next available block out of the list
  722.  
  723.                     if (__queuein != __queueout) {
  724.  
  725.                         _file_data_length     = __queuelen[__queueout];
  726.                         __singleblockpointer = __queuebuff[__queueout];
  727.                     }
  728.                     else
  729.                         loop = FALSE;
  730.                 }
  731.  
  732.             // we are now done with this much of the buffer, stop if zero
  733.  
  734.                 if (!(TargetSize -= n))
  735.                     loop = FALSE;
  736.  
  737.         }
  738.  
  739.     // advance the list to the next DMA buffer and count one more...
  740.  
  741.         __NextPtr = __NextPtr->nextptr;
  742.  
  743.     // if we can do more, then DO IT!!!
  744.  
  745.         if (BufferDataCount > 0) {                // if there is data in the DMA...
  746.             if (__queueincnt)                    // if we have buffers...
  747.                     goto nextblock;             // then go load it...
  748.         }
  749.  
  750.     // return the number of bytes loaded
  751.  
  752.         return(result);
  753.  
  754. }
  755. #endif
  756.  
  757.  
  758. #if FILEIN
  759. ;    /*\
  760. ;---|*|----====< ContinueFileInput >====----
  761. ;---|*|
  762. ;---|*| This routine checks to see if another buffer can be written to disk.
  763. ;---|*| if so, it will load copy the buffer to a local buffer, then write it
  764. ;---|*| out to disk. A return value of 0 indicates recording has stopped,
  765. ;---|*| which could mean that the disk file is full, so the DMA had to be
  766. ;---|*| stopped prematurely.
  767. ;---|*|
  768. ;    \*/
  769. int ContinueFileInput()
  770. {
  771.  
  772.     /* if BufferDataCount is non-zero, we must write out the data        */
  773.  
  774.         while (BufferDataCount) {
  775.  
  776.             /* data is available, move it out to disk                    */
  777.  
  778.             /* if not all data is written, return in error                */
  779.  
  780.                 if (_dofwrite (__NextPtr->buffer,BufferSize,fileno(__fptr)) != BufferSize) {
  781.                     StopDMAIO();
  782.                     return (0);
  783.                 }
  784.  
  785.             /* move to the next buffer                                  */
  786.  
  787.                 __NextPtr->status = 0;
  788.                 __NextPtr = __NextPtr->nextptr;
  789.                 BufferDataCount--;
  790.         }
  791.  
  792.     /* if at the end of this block, reposition the stream pointer       */
  793.  
  794.         if (!DMARunning)
  795.             fseek ( __fptr, __fptrpos, SEEK_SET );
  796.  
  797.         return (DMARunning);
  798. }
  799. #endif
  800.  
  801.  
  802. #if BLOCKOUT
  803. ;    /*\
  804. ;---|*|----====< ContinueBlockOutput >====----
  805. ;---|*|
  806. ;---|*| This routine checks to see if another DMA buffer can be loaded.
  807. ;---|*| If so, it will load the user's block data into an empty buffer.
  808. ;---|*| A return value of 1 indicates the buffer has been loaded, else
  809. ;---|*| it must be sent in again until it is loaded.
  810. ;---|*|
  811. ;    \*/
  812. int ContinueBlockOutput(buff)
  813.     char far *buff;
  814. {
  815.  
  816.     /* if the internal count is not max-ed out, try to load the next buffer */
  817.  
  818.         if (BufferDataCount < MaxBuffCount ) {
  819.  
  820.             _loadtheblock (buff);
  821.  
  822.             if (DMARunning == 0) {        /* yuck! a DMA break!            */
  823.                 _resetbuffers();
  824.                 StartTheDMAOutput(0);
  825.             }
  826.  
  827.             return (1);                 /* return running                */
  828.         }
  829.         else
  830.             return(0);
  831. }
  832.  
  833.  
  834. ;    /*\
  835. ;---|*|----====< ContinueThisBlockOutput >====----
  836. ;---|*|
  837. ;---|*| This routine checks to see if another DMA buffer can be loaded.
  838. ;---|*| If so, it will load the user's block data into an empty buffer.
  839. ;---|*| A return value of ~0 indicates the buffer has been loaded.
  840. ;---|*|
  841. ;---|*| The foreground routine will call this when DMARunning == 0. The
  842. ;---|*| background routine will call this at every interrupt to keep the
  843. ;---|*| buffers loaded
  844. ;---|*|
  845. ;    \*/
  846. int ContinueThisBlockOutput()
  847. {
  848. unsigned int n,          // working integer
  849.     TargetSize, // size of the target dma buffer
  850.     loop,        // loop flag to keep loading blocks
  851.     bcount;     // increments the BufferDataCount
  852. unsigned int result = 0; // holds the final count
  853. char far *s;
  854.  
  855.     // If no more data to load in the buffer, flush the next DMA & return
  856.  
  857.         if (__queueincnt == 0) {
  858. //__debugdw (0x20);
  859.             FlushBuffer (__NextPtr->buffer,BufferSize);
  860.             __NextPtr = __NextPtr->nextptr;
  861.             return(0);
  862.         }
  863.  
  864.     // if there is little data, but a lot in the DMA, just return
  865.  
  866.         if ((__queuedata < BufferSize) && (BufferDataCount > 2)) {
  867. //__debugdw (0x21);
  868.             return(0);
  869.         }
  870.  
  871.     // if the DMA has been turned off, re-sync the buffers
  872.  
  873.         if (DMARunning == 0) {
  874. //__debugdw (0x29);
  875.             _resetbuffers();
  876.         }
  877.  
  878.     // if the current remaining length is null, prime for the next block
  879.  
  880.         if (_file_data_length == 0) {
  881. //__debugdw (0x2A,__queueout);
  882.             _file_data_length     = __queuelen [__queueout];
  883.             __singleblockpointer = __queuebuff[__queueout];
  884.         }
  885.  
  886.     // loop here to stuff as many blocks as possible into the DMA buffer
  887.  
  888.     nextblock:
  889.  
  890.     // move up to one buffer division worth of data
  891.  
  892.         TargetSize = BufferSize;
  893.         s = __NextPtr->buffer;
  894.         loop = TRUE;
  895.         bcount = 1;
  896.  
  897.     // move as many blocks as possible into the DMA buffer
  898.  
  899.         while (loop) {
  900.  
  901. //__debugdw (0x22);
  902.  
  903.             // Get the block length, up to the division size
  904.  
  905.                 if (_file_data_length <= TargetSize) {
  906.  
  907.                     n = _file_data_length;    // full target size
  908.                     _file_data_length = 0;
  909.  
  910.                 }
  911.                 else                        // partial target size
  912.  
  913.                     _file_data_length -= (n = TargetSize);
  914.  
  915. //__debugdw (0x23,n);
  916.  
  917.             // copy the data to the buffer, and advance the buffer that far
  918.  
  919.                 if (n) {
  920.  
  921.                     s = _rfmemcpy(s, __singleblockpointer, n );
  922.  
  923.                     result += n;                // more for the return value
  924.                     __singleblockpointer += n;    // advance the pointer
  925.                     __queuedata -= (n &0xffff); // less queued up
  926.  
  927.                     BufferDataCount += bcount;  // increment buffer count once
  928.                     bcount = 0;
  929.  
  930.                 }
  931.                 else
  932.                     s = __NextPtr->buffer;        // no data, but do point here
  933.  
  934.             // if the length is zero, this buffer is done, let the caller know
  935.  
  936.                 if (!_file_data_length) {
  937.  
  938. //__debugdw (0x24);
  939.  
  940.                     // if this old block was valid, send a DONE msg.
  941.  
  942.                     if(__queueincnt) {
  943.  
  944.                         // let the app. know we are done with this buffer
  945.  
  946.                         if (__queuecb[__queueout])
  947.                             (*__queuecb[__queueout])(__queuebuff[__queueout],FALSE);
  948.  
  949.                         __queueincnt--;
  950.                         __queueout = ++__queueout & QUEUEMASK;
  951.                     }
  952.  
  953.                     // Now, try to get the next available block out of the list
  954.  
  955.                     if (__queuein == __queueout) {
  956.  
  957. //__debugdw (0x25);
  958.  
  959.                         FlushBuffer (s,TargetSize-n);
  960.                         loop = FALSE;
  961.  
  962.                     }
  963.                     else {
  964.  
  965. //__debugdw (0x26);
  966.  
  967.                         _file_data_length     = __queuelen[__queueout];
  968.                         __singleblockpointer = __queuebuff[__queueout];
  969.  
  970.                     }
  971.                 }
  972.  
  973.             // we are now done with this much of the buffer, stop if zero
  974.  
  975.                 if (!(TargetSize -= n))
  976.                     loop = FALSE;
  977.         }
  978.  
  979.     // advance the list to the next DMA buffer and count one more...
  980.  
  981.         __NextPtr = __NextPtr->nextptr;
  982.  
  983.     // if we can do more, then DO IT!!!
  984.  
  985.         if (BufferDataCount < MaxBuffCount) {    // if there is room in the DMA
  986.             if (__queueincnt) {                 // if we have pcm data
  987.                 if (__queuedata >= BufferSize){ // and its GE a buffer division,
  988.  
  989. //__debugdw (0x27);
  990.                     goto nextblock;             // then go load it...
  991.                 }
  992.             }
  993.         }
  994.  
  995.         if (DMARunning == 0) {
  996.  
  997. //__debugdw (0x28);
  998.  
  999.             StartTheDMAOutput(ContinueThisBlockOutput);
  1000.         }
  1001.  
  1002.     // return the number of bytes loaded
  1003.  
  1004.         return(result);
  1005.  
  1006. }
  1007. #endif
  1008.  
  1009.  
  1010. #if FILEOUT
  1011. ;    /*\
  1012. ;---|*|----====< ContinueFileOutput >====----
  1013. ;---|*|
  1014. ;---|*| This routine checks to see if another buffer can be loaded. If so, it
  1015. ;---|*| will load the data into an empty buffer. All empty buffers will be
  1016. ;---|*| loaded. A return value of 0 indicates playing has finished.
  1017. ;---|*|
  1018. ;    \*/
  1019. int ContinueFileOutput()
  1020. {
  1021.  
  1022.     /* if BufferDataCount is not max-ed out, try to load the next buffer*/
  1023.  
  1024.         if (BufferDataCount < MaxBuffCount ) {
  1025.  
  1026.             if (_loadthebuffer()) {
  1027.  
  1028.                 if (DMARunning == 0) {     /* yuck! a DMA break!            */
  1029.                     _resetbuffers();
  1030.                     if (StartTheDMAOutput(0))
  1031.                         return(0);
  1032.                 }
  1033.             }
  1034.         }
  1035.  
  1036.     /* if at the end of this block, reposition the stream pointer        */
  1037.  
  1038.         if (!DMARunning)
  1039.             fseek ( __fptr, __fptrpos, SEEK_SET );
  1040.  
  1041.         return (DMARunning);            /* return the DMA state         */
  1042. }
  1043. #endif
  1044.  
  1045.  
  1046. #if COMMDATA
  1047. ;    /*\
  1048. ;---|*|----====< OpenPCMBuffering >====----
  1049. ;---|*|
  1050. ;---|*|  This routine is the first-call routine. It initializes the buffers
  1051. ;---|*|  needed for the PCM play/record system. A return value of non-zero
  1052. ;---|*|  indicates a failure to initialize the system.
  1053. ;---|*|
  1054. ;---|*|  Entry Conditions:
  1055. ;---|*|
  1056. ;---|*|         dma -- New DMA #. (1-3, or -1 for no changes)
  1057. ;---|*|         irq -- New IRQ #. (3,5,6,7, or -1 for no changes)
  1058. ;---|*|
  1059. ;---|*|  Exit Conditions:
  1060. ;---|*|
  1061. ;---|*|         non-zero return indicates an error
  1062. ;---|*|
  1063. ;    \*/
  1064. int OpenPCMBuffering(dma,irq,dmasize,divisions)
  1065.    int dma;         /* DMA channel # (-1 for no changes)    */
  1066.    int irq;         /* IRQ channel # (-1 for no changes)    */
  1067.    int dmasize;     /* requested DMA size (4/8/16/32/64)    */
  1068.    int divisions;    /* # of divisions in the DMA buffer     */
  1069. {
  1070. BuffPtr op,p;
  1071. long l;
  1072. int n;
  1073. char far *db;
  1074.  
  1075. //__debugdw (0x00);
  1076.  
  1077.     /* setup the globa variables & a local buffer                        */
  1078.  
  1079.         MaxBuffCount = divisions;
  1080.         BufferSize     = LONG(dmasize/divisions) * 1024L;
  1081.  
  1082.     /* Setup the lowlevel routines                                        */
  1083.  
  1084.         InitMVSound();
  1085.  
  1086.     /* flush any background task setup                                  */
  1087.  
  1088.         BackgroundInit( BufferSize, MaxBuffCount );
  1089.  
  1090.     /* Allocate twice the size for background DMA buffer                */
  1091.  
  1092.         l = LONG(dmasize) * 1024 * 2;
  1093.  
  1094.         if ((DMABuffPtr = (char huge *)_memmalloc(l)) == 0)
  1095.             return(PCMIOERR_NOMEM);
  1096.  
  1097.         if ((db=StartOfDMABuffer=FindDMABuffer(DMABuffPtr,dmasize)) == 0)
  1098.             return (PCMIOERR_OPENPCM);
  1099.  
  1100.     /* if the low level code doesn't like it, bomb out                  */
  1101.  
  1102.         if (!DMABuffer ( StartOfDMABuffer, dmasize, MaxBuffCount ))
  1103.             return(PCMIOERR_OPENPCM);
  1104.  
  1105.     /* Attempt to allocate each foreground buffer                        */
  1106.  
  1107.         op = 0;
  1108.         for (n=0;n<divisions;n++) {
  1109.  
  1110.             /* allocate the linked list header for each buffer            */
  1111.  
  1112.                 if ((p = (BuffPtr)_memmalloc (sizeof(BuffData))) == 0)
  1113.                     return(PCMIOERR_NOMEM);
  1114.  
  1115.             /* reset the pointer in case of other failures during init    */
  1116.  
  1117.                 p->nextptr = 0;
  1118.  
  1119.             /* if first block, save as the head of the list             */
  1120.  
  1121.                 if (!HeadOfBuffers)
  1122.                     HeadOfBuffers = p;
  1123.  
  1124.             /* if we have already allocated a block, setup the fwd ptr    */
  1125.  
  1126.                 if (op)
  1127.                     op->nextptr = p;
  1128.  
  1129.                 p->buffer = db;
  1130.                 p->size   = BufferSize;
  1131.                 db          += BufferSize;
  1132.  
  1133.             /* save as the old pointer for linking purposes             */
  1134.  
  1135.                 op = p;
  1136.         }
  1137.  
  1138.     /* link the last buffer back to the first                            */
  1139.  
  1140.         p->nextptr = HeadOfBuffers;
  1141.  
  1142.     /* Possibly select new DMA & IRQ channels                            */
  1143.  
  1144.         if (dma != -1)
  1145.             if (SelectDMA(dma))
  1146.                 return(PCMIOERR_BADDMA);
  1147.  
  1148.         if (irq != -1)
  1149.             if (SelectIRQ(irq))
  1150.                 return(PCMIOERR_BADIRQ);
  1151.  
  1152.     /* well, it looks good so far, flush any variables                    */
  1153.  
  1154.         BufferDataCount   = ProcessedBlockCount =
  1155.         _file_data_length = __queuedata         =
  1156.         VoiceActivatedSavedCount = __queueincnt =
  1157.         __queuein  = __queueout = 0;
  1158.  
  1159.     /* and return good!                                                 */
  1160.  
  1161.         return (0);
  1162. }
  1163. #endif
  1164.  
  1165.  
  1166. #if COMMDATA
  1167. ;    /*\
  1168. ;---|*|----====< PCMState >====----
  1169. ;---|*|
  1170. ;---|*| This routine passes in the sample rate, stereo/mono flag, and any
  1171. ;---|*| other miscellaneous data (to be determined later...)
  1172. ;---|*|
  1173. ;---|*| Exit Conditions:
  1174. ;---|*|    Non-zero means the sample rate was in error.
  1175. ;---|*|    Zero means the sample rate was okay error.
  1176. ;---|*|
  1177. ;    \*/
  1178. int PCMState(sr,sm,cp,sz)
  1179.     long sr;    /* sample rate    */
  1180.     int  sm;    /* stereo/mono    */
  1181.     int  cp;    /* compression    */
  1182.     int  sz;    /* size(8/16)    */
  1183. {
  1184.  
  1185. //__debugdw (0x02);
  1186.  
  1187.     /* just pass them on...                                             */
  1188.  
  1189.         __pcmdatasize = sz;                     /* pcm data size        */
  1190.  
  1191.         return (PCMInfo(sr,sm,cp,sz) ? PCMIOERR_SAMPLERATE : 0 );
  1192.  
  1193. }
  1194. #endif
  1195.  
  1196.  
  1197. #if COMMDATA
  1198.  
  1199. ;   /*\
  1200. ;---|*|----====< QueryPCMStream >====----
  1201. ;---|*|
  1202. ;---|*| Returns the number of blocks playing in the DMA buffer and
  1203. ;---|*| the number of blocks queued up.
  1204. ;---|*|
  1205. ;---|*| Exit Conditions:
  1206. ;---|*|    0 - x is the number of blocks waiting to be played
  1207. ;---|*|    -1 DMA is stopped, no more data
  1208. ;---|*|
  1209. ;    \*/
  1210. int QueryPCMStream()
  1211. {
  1212.  
  1213. //__debugdw (0x15,__queueincnt+BufferDataCount);
  1214.  
  1215.     // done if no DMA activity
  1216.  
  1217.         if (!DMARunning)
  1218.             return(-1);
  1219.  
  1220.     // return the count of active blocks
  1221.  
  1222.         return (__queueincnt+BufferDataCount);
  1223.  
  1224. }
  1225. #endif
  1226.  
  1227.  
  1228. #if COMMDATA
  1229. ;    /*\
  1230. ;---|*|----====< StopDMAIO >====----
  1231. ;---|*|
  1232. ;---|*| This routine forceably kills the PCM I/O. All buffers will be
  1233. ;---|*| reset, the current position of the input file is not altered. There
  1234. ;---|*| is no return value.
  1235. ;---|*|
  1236. ;    \*/
  1237. void StopDMAIO()
  1238. {
  1239.  
  1240. //__debugdw (0x03);
  1241.  
  1242.     /* if this code has not already been setup, exit now                */
  1243.  
  1244.         if (!HeadOfBuffers)
  1245.             return;
  1246.  
  1247.     /* stop the hardware...                                             */
  1248.  
  1249.         StopPCM( );
  1250.  
  1251.         __queuein    = __queueincnt = __queueout = DMARunning = 0;
  1252.         __queuedata = _file_data_length = 0;
  1253.  
  1254.     /* flush any prior background task setup                            */
  1255.  
  1256.     ////if (__DirectionFlag == DMAOUTPUT) {
  1257.     ////    if (__fptr) {
  1258.     ////        rewind    (__fptr);
  1259.     ////        __fptrpos = 0;
  1260.     ////    }
  1261.     ////}
  1262.  
  1263.     /* reset the linked list of buffers                                 */
  1264.  
  1265.         _resetbuffers();
  1266.  
  1267.     /* setup our internal direction flag                                */
  1268.  
  1269.         __DirectionFlag = NODIRECTION;
  1270.  
  1271. }
  1272. #endif
  1273.  
  1274.  
  1275. #if BLOCKIN
  1276. ;    /*\
  1277. ;---|*|----====< StartBlockInput >====----
  1278. ;---|*|
  1279. ;---|*| This routine resets the buffer pointers, then starts up
  1280. ;---|*| the DMA PCM input. Nothing else needs to be done. A return
  1281. ;---|*| value of 0 indicates the DMA failed to startup; No input
  1282. ;---|*| is occuring.
  1283. ;---|*|
  1284. ;    \*/
  1285. int StartBlockInput()
  1286. {
  1287.  
  1288.     /* setup our internal direction flag                                */
  1289.  
  1290.         __DirectionFlag = DMAINPUT;
  1291.  
  1292.     /* Reset the # of blocks seen during I/O processing                 */
  1293.  
  1294.         ProcessedBlockCount = 0;
  1295.  
  1296.     /* Flush all buffers                                                */
  1297.  
  1298.         _resetbuffers();
  1299.  
  1300.     /* if the hardware level code isn't gonna work, then return a failure. */
  1301.  
  1302.         return (!StartTheDMAInput(0));
  1303. }
  1304.  
  1305.  
  1306. ;    /*\
  1307. ;---|*|----====< RecordThisBlock >====----
  1308. ;---|*|
  1309. ;---|*| This routine offers the caller a simplified recording of one
  1310. ;---|*| variable length block of data. The call just needs to call
  1311. ;---|*| OpenPCMBuffering, then RecordThisBlock. The caller just has
  1312. ;---|*| to poll DMARunning to see if the block has completed. Calling
  1313. ;---|*| StopDMAIO will stop the process, if need be.
  1314. ;---|*|
  1315. ;    \*/
  1316. int RecordThisBlock(p,len,cb)
  1317.     char far *p;
  1318.     unsigned long len;
  1319.     void (far *cb)();
  1320. {
  1321. int n;
  1322.  
  1323.     // if the pointer is valid, then queue it up
  1324.  
  1325.         if (p) {
  1326.  
  1327.             // return if already full
  1328.  
  1329.             if (__queuein == QUEUESIZE)
  1330.                 return(2);
  1331.  
  1332.             // extract the 1st entry from the queue
  1333.  
  1334.             __queuebuff[__queuein] = p;
  1335.             __queuedata += (__queuelen [__queuein] = len);
  1336.             __queuecb  [__queuein] = cb;
  1337.             __queuein  = ++__queuein & QUEUEMASK;
  1338.             __queueincnt++;
  1339.  
  1340.         }
  1341.  
  1342.     // if the blocks are not recording , the let'er rip...
  1343.  
  1344.         if ((DMARunning == 0) && __queueincnt ) {
  1345.  
  1346.             // setup the direction flag
  1347.  
  1348.                 __DirectionFlag = DMAINPUT;
  1349.  
  1350.             /* reset the DMA block pointers                             */
  1351.  
  1352.                 _resetbuffers();
  1353.  
  1354.             /* return good or bad if the PCM engine is running            */
  1355.  
  1356.                 return (ContinueThisBlockInput() ? 1 : 0 );
  1357.  
  1358.         }
  1359.  
  1360.     // assume the block is now recording
  1361.  
  1362.         return(0);
  1363.  
  1364. }
  1365. #endif
  1366.  
  1367.  
  1368. #if FILEIN
  1369. ;    /*\
  1370. ;---|*|----====< StartFileInput >====----
  1371. ;---|*|
  1372. ;---|*| This routine resets the buffer pointers, then starts up the DMA PCM
  1373. ;---|*| input. Nothing else needs to be done.
  1374. ;---|*|
  1375. ;    \*/
  1376. int StartFileInput(f)
  1377.    FILE *f;
  1378. {
  1379.  
  1380.     /* save our local file handle                                        */
  1381.  
  1382.         __fptr      = f;
  1383.         __fptrpos = ftell(f);
  1384.  
  1385.     /* setup our internal direction flag                                */
  1386.  
  1387.         __DirectionFlag = DMAINPUT;
  1388.  
  1389.     /* Reset the # of blocks seen during I/O processing                 */
  1390.  
  1391.         ProcessedBlockCount = 0;
  1392.  
  1393.     /* Flush all buffers and other stuff..                                */
  1394.  
  1395.         _resetbuffers();
  1396.         VoiceActivatedSavedCount = 0;
  1397.  
  1398.     /* start the DMA engine                                             */
  1399.  
  1400.         return (!StartTheDMAInput(0));
  1401. }
  1402. #endif
  1403.  
  1404.  
  1405. #if BLOCKOUT
  1406. ;    /*\
  1407. ;---|*|----====< StartBlockOutput >====----
  1408. ;---|*|
  1409. ;---|*| This routine allocates and loads the necessary buffers with data from
  1410. ;---|*| the PCM disk file. Upon return, if there is data available, the
  1411. ;---|*| background task will be called to start the DMA. The file handle will
  1412. ;---|*| be saved in a global variable to be access from other foreground
  1413. ;---|*| routines. a non-zero return value indicates PCM output is playing.
  1414. ;---|*|
  1415. ;    \*/
  1416. int StartBlockOutput(buff)
  1417.     char far *buff;
  1418. {
  1419.  
  1420.     /* setup our internal direction flag                                */
  1421.  
  1422.         __DirectionFlag = DMAOUTPUT;
  1423.  
  1424.     /* Reset the # of blocks seen during I/O processing                 */
  1425.  
  1426.         ProcessedBlockCount = 0;
  1427.  
  1428.     /* load the DMA buffers                                             */
  1429.  
  1430.         _resetbuffers();
  1431.         _loadtheblock (buff);
  1432.  
  1433.     /* return good or bad if the engine is started                        */
  1434.  
  1435.         return (!StartTheDMAOutput(0));
  1436. }
  1437.  
  1438.  
  1439. ;    /*\
  1440. ;---|*|----====< PlayThisBlock >====----
  1441. ;---|*|
  1442. ;---|*| This routine offers the caller a simplified playback of one
  1443. ;---|*| variable length block of data. The call just needs to call
  1444. ;---|*| OpenPCMBuffering, then PlayThisBlock. The caller just has
  1445. ;---|*| to poll DMARunning to see if the block has completed. Calling
  1446. ;---|*| StopDMAIO will stop the process, if need be.
  1447. ;---|*|
  1448. ;---|*| Also see QueueThisBlock.
  1449. ;---|*|
  1450. ;---|*| Entry:
  1451. ;---|*|    p   is the far pointer to a block of data (64k max size). If
  1452. ;---|*|        the pointer is null, the block will not be queued.
  1453. ;---|*|    len is the length of the block in bytes (one based count).
  1454. ;---|*|    cb  is the callback routine to call when the block is empty.
  1455. ;---|*|
  1456. ;---|*| Returns:
  1457. ;---|*|    0 - block is queued and playing
  1458. ;---|*|    1 - Block failed to start
  1459. ;---|*|    2 - queue is full, try later
  1460. ;---|*|
  1461. ;    \*/
  1462. int PlayThisBlock(p,len,cb)
  1463.     char far *p;
  1464.     unsigned long len;
  1465.     void (far *cb)();
  1466. {
  1467. int n;
  1468.  
  1469.     // if the pointer is valid, then queue it up
  1470.  
  1471.         if (p) {
  1472.  
  1473.             // return if already full
  1474.  
  1475.             if (__queuein == QUEUESIZE)
  1476.                 return(2);
  1477.  
  1478.             // extract the 1st entry from the queue
  1479.  
  1480.             __queuebuff[__queuein] = p;
  1481.             __queuedata += (__queuelen [__queuein] = len);
  1482.             __queuecb  [__queuein] = cb;
  1483.             __queuein  = ++__queuein & QUEUEMASK;
  1484.             __queueincnt++;
  1485.  
  1486. //__debugdw (0x10,__queueincnt);
  1487. //__debugdw (0x14,BufferDataCount);
  1488.  
  1489.         }
  1490.  
  1491.     // if the blocks are not playing, the let'er rip...
  1492.  
  1493.         if ((DMARunning == 0) && __queueincnt ) {
  1494.  
  1495. //__debugdw (0x11);
  1496.  
  1497.             // setup to transfer this block
  1498.  
  1499.                 __DirectionFlag = DMAOUTPUT;
  1500.  
  1501.             // start the process by loading the DMA buffers
  1502.  
  1503.                 return (ContinueThisBlockOutput() ? 0 : 1 );
  1504.  
  1505.         }
  1506.  
  1507.     // Indicate this has been queued up
  1508.  
  1509.         return(0);
  1510. }
  1511.  
  1512.  
  1513. ;    /*\
  1514. ;---|*|----====< QueueThisBlock >====----
  1515. ;---|*|
  1516. ;---|*| This routine will queue up one block, but not start the PCM
  1517. ;---|*| transfers. Once X number of blocks are queued up, then the caller
  1518. ;---|*| can call PlayThisBlock/RecordThisBlock to start the process.
  1519. ;---|*|
  1520. ;---|*| Also see PlayThisBlock/RecordThisBlock.
  1521. ;---|*|
  1522. ;---|*| Entry:
  1523. ;---|*|    p   is the far pointer to a block of data (64k max size).
  1524. ;---|*|        All pointers are assumed to be valid.
  1525. ;---|*|    len is the length of the block in bytes (one based count).
  1526. ;---|*|    cb  is the callback routine to call when the block is empty.
  1527. ;---|*|
  1528. ;---|*| Returns:
  1529. ;---|*|    0 - block is queued and playing
  1530. ;---|*|    2 - queue is full, try later
  1531. ;---|*|
  1532. ;    \*/
  1533. int QueueThisBlock(p,len,cb)
  1534.     char far *p;
  1535.     unsigned long len;
  1536.     void (far *cb)();
  1537. {
  1538. int n;
  1539.  
  1540.     // return if already full
  1541.  
  1542.         if (__queuein == QUEUESIZE) {
  1543.  
  1544. //__debugdw (0x12,__queueincnt);
  1545.  
  1546.             return(2);
  1547.  
  1548.         }
  1549.  
  1550.     // if idle, setup our internal direction flag, and the desired length
  1551.  
  1552.         __queuebuff[__queuein] = p;
  1553.         __queuedata += (__queuelen [__queuein] = len);
  1554.         __queuecb  [__queuein] = cb;
  1555.         __queuein  = ++__queuein & QUEUEMASK;
  1556.         __queueincnt++;
  1557.  
  1558. //__debugdw (0x12,__queueincnt);
  1559.  
  1560.     // return all queued up
  1561.  
  1562.         return(0);
  1563.  
  1564. }
  1565.  
  1566.  
  1567. ;    /*\
  1568. ;---|*|----====< SyncCallBack >====----
  1569. ;---|*|
  1570. ;---|*| This routine will setup a callback to the caller's routine
  1571. ;---|*| at the end of every DMA block interrupt
  1572. ;---|*|
  1573. ;---|*| Returns:
  1574. ;---|*|
  1575. ;    \*/
  1576. int SyncCallBack(cb)
  1577.     void (far *cb)();
  1578. {
  1579. int n;
  1580.  
  1581.     // just save for a later call...
  1582.  
  1583.         __synccallback = cb;
  1584.  
  1585. }
  1586.  
  1587. #endif
  1588.  
  1589.  
  1590. #if FILEOUT
  1591. ;    /*\
  1592. ;---|*|----====< StartFileOutput >====----
  1593. ;---|*|
  1594. ;---|*| This routine allocates and loads the necessary buffers with data from
  1595. ;---|*| the PCM disk file. Upon return, if there is data available, the
  1596. ;---|*| background task will be called to start the DMA. The file handle will
  1597. ;---|*| be saved in a global variable to be access from other foreground
  1598. ;---|*| routines. a non-zero return value indicates PCM output is playing.
  1599. ;---|*|
  1600. ;    \*/
  1601. int StartFileOutput(f,len)
  1602.     FILE *f;
  1603.     long len;
  1604. {
  1605.  
  1606.     /* save our local file handle                                        */
  1607.  
  1608.         __fptr = f;
  1609.         __fptrpos = ftell(f);
  1610.  
  1611.     /* setup our internal direction flag, and the desired length        */
  1612.  
  1613.         __DirectionFlag   = DMAOUTPUT;
  1614.         _file_data_length = len;
  1615.  
  1616.     /* Reset the # of blocks seen during I/O processing                 */
  1617.  
  1618.         ProcessedBlockCount = 0;
  1619.  
  1620.     /* if any data is loaded into the buffer, start the DMA & return    */
  1621.  
  1622.         _resetbuffers();
  1623.  
  1624.         do {
  1625.  
  1626.             /* get the next buffer full & exit if done                    */
  1627.  
  1628.             if (!_loadthebuffer())
  1629.                 break;
  1630.  
  1631.         } while (__NextPtr != HeadOfBuffers);
  1632.  
  1633.     /* return good or bad if the engine is running                        */
  1634.  
  1635.         return (!StartTheDMAOutput(0));
  1636.  
  1637. }
  1638. #endif
  1639.  
  1640.  
  1641. #if BLOCKOUT
  1642. ;    /*\
  1643. ;---|*|----====< _loadtheblock >====----
  1644. ;---|*|
  1645. ;---|*| This routine loads the block into the DMA buffer.
  1646. ;---|*|
  1647. ;    \*/
  1648. static int _loadtheblock(buff)
  1649.     char far *buff;
  1650. {
  1651.  
  1652.     /* load the block of data into the DMA buffer                        */
  1653.  
  1654.         _rfmemcpy(__NextPtr->buffer, buff, BufferSize );
  1655.  
  1656.     /* now that the data is secure, fill out the rest of the header to    */
  1657.     /* allow the background process to "see" this new data              */
  1658.  
  1659.         __NextPtr->status = 1;
  1660.         __NextPtr->count  = BufferSize;
  1661.         __NextPtr          = __NextPtr->nextptr; /* advance the list     */
  1662.         BufferDataCount++;
  1663.  
  1664.     /* we have data, return the size                                    */
  1665.  
  1666.        return (BufferSize);
  1667. }
  1668. #endif
  1669.  
  1670.  
  1671. #if FILEOUT
  1672. ;    /*\
  1673. ;---|*|----====< _loadthebuffer >====----
  1674. ;---|*|
  1675. ;---|*| This routine loads the disk contents into an available buffer.
  1676. ;---|*| A return value of 0 indicates no more data has been loaded.
  1677. ;---|*|
  1678. ;    \*/
  1679. static int _loadthebuffer()
  1680. {
  1681. char huge *s;
  1682. register int n;
  1683. long l;
  1684.  
  1685.     /* reset the header data                                            */
  1686.  
  1687.         __NextPtr->count = __NextPtr->status = 0;
  1688.  
  1689.     /* exit if there is no data to be read                                */
  1690.  
  1691.         if (feof (__fptr) || (!_file_data_length))
  1692.             return (0);
  1693.  
  1694.     /* adjust the max count we want to process from the file            */
  1695.  
  1696.         if (_file_data_length <= BufferSize) {
  1697.             l = _file_data_length;
  1698.             _file_data_length = 0;
  1699.         }
  1700.         else {
  1701.             _file_data_length -= (l = (BufferSize & 0xffff));
  1702.         }
  1703.  
  1704.     /* read the data from the file                                        */
  1705.  
  1706.         if((n = _dofread (__NextPtr->buffer, ((int)(l & 0xffff)),fileno(__fptr) )) == 0)
  1707.             return(0);
  1708.  
  1709.         s = __NextPtr->buffer+n; // point to the end of the block
  1710.  
  1711.     // flush to the end if not a full buffer worth of data
  1712.  
  1713.         if (n < BufferSize)
  1714.             FlushBuffer (s,BufferSize-n);
  1715.  
  1716.     /* now that the data is secure, fill out the rest of the header to    */
  1717.     /* allow the background process to "see" this new data              */
  1718.  
  1719.         __NextPtr->status = 1;
  1720.         __NextPtr->count  = BufferSize;
  1721.         __NextPtr          = __NextPtr->nextptr; /* advance the list     */
  1722.         BufferDataCount++;
  1723.  
  1724.     /* we have data, return the size                                    */
  1725.  
  1726.        return (n);
  1727. }
  1728. #endif
  1729.  
  1730.  
  1731. #if COMMDATA
  1732. ;    /*\
  1733. ;---|*|----====< _resetbuffers >====----
  1734. ;---|*|
  1735. ;---|*| This routine flushes the contents of the top level buffers
  1736. ;---|*|
  1737. ;    \*/
  1738. _resetbuffers()
  1739. {
  1740.  
  1741.     /* flush the count and status of every linked list block            */
  1742.  
  1743.         if ((__NextPtr = HeadOfBuffers) != 0) {
  1744.  
  1745.             /* if there are buffers, reset them all                     */
  1746.  
  1747.             do {
  1748.  
  1749.                 /* get the next buffer full & exit if done                */
  1750.  
  1751.                     __NextPtr->count = __NextPtr->status = 0;
  1752.  
  1753.                 /* break when we reach the top                            */
  1754.  
  1755.                     if ((__NextPtr = __NextPtr->nextptr) == 0)
  1756.                             break;
  1757.  
  1758.             } while (__NextPtr != HeadOfBuffers);
  1759.  
  1760.         }
  1761.  
  1762.     /* reset the global hand shake count.                                */
  1763.  
  1764.         BufferDataCount = 0;
  1765. }
  1766.  
  1767. int __debugdw (int idx,int val)
  1768. {
  1769. #if 0
  1770.  
  1771.     _asm {
  1772.  
  1773.         push    di
  1774.         push    es
  1775.  
  1776.         sub     di,di
  1777.         mov     es,di
  1778.         les     di,es:[0x194]
  1779.  
  1780.         mov     ax,es
  1781.         or        ax,ax
  1782.         jz        dedw05
  1783.  
  1784.         cmp     di,16768
  1785.         jae     dedw05
  1786.  
  1787.         cld
  1788.         mov     ax,idx
  1789.         stosb
  1790.         mov     ax,val
  1791.         stosw
  1792.  
  1793.         sub     ax,ax
  1794.         mov     es,ax
  1795.         mov     es:[0x194],di
  1796.  
  1797.     } dedw05: _asm {
  1798.  
  1799.         pop     es
  1800.         pop     di
  1801.  
  1802.     }
  1803. #endif
  1804. }
  1805.  
  1806. #endif
  1807.  
  1808. ;   /*\
  1809. ;---|*| end of PCMIOC.C
  1810. ;    \*/
  1811.  
  1812.